package com.service.business.order.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.service.business.member.service.MemberStoreService;
import com.service.business.order.controller.req.OrderProductOtherCreateReq;
import com.service.business.order.controller.req.OrderProductOtherReq;
import com.service.business.order.dto.OrderProductOtherTotalVo;
import com.service.business.order.service.OrderProductOtherTotalService;
import com.service.business.order.utils.OrderUtils;
import com.service.business.pay.controller.rep.PayRecord;
import com.service.business.pay.service.PayService;
import com.service.business.product.service.ProductPayTypeService;
import com.service.business.support.Language;
import com.service.business.utils.I18nUtil;
import com.service.dao.constants.ConfigConstants;
import com.service.business.base.dto.LoginUserSession;
import com.service.dao.enums.*;
import com.service.dao.model.*;
import com.service.business.base.service.ConfigService;
import com.service.core.exception.CommonException;
import com.service.core.rest.response.CommonPageRep;
import com.service.dao.mapper.OrderProductOtherMapper;
import com.service.business.member.service.MemberAddressService;
import com.service.business.member.service.MemberCacheService;
import com.service.business.order.controller.rep.BaseOrderRep;
import com.service.business.order.controller.rep.OrderProductRep;
import com.service.business.order.controller.req.OrderProductPageReq;
import com.service.business.order.dto.ProductOtherCar;
import com.service.business.order.service.OrderProductOtherDetailsService;
import com.service.business.order.service.OrderProductOtherService;
import com.service.business.product.service.ProductOtherService;
import com.service.business.support.SessionUtil;
import com.service.business.support.ShoppingCar;
import com.service.dao.po.MemberCache;
import com.service.utility.common.UUIDUtil;
import com.service.utility.modelmapper.ModelMapperUtil;
import com.service.business.utils.DateUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <dl>
 * <dt>back-end</dt>
 * <dd>Description:</dd>
 * <dd>Copyright: Copyright (C) 2015</dd>
 * <dd>Company: </dd>
 * <dd>CreateDate: 2019年09月29日</dd>
 * </dl>
 *
 * @author
 */
@Slf4j
@Service
public class OrderProductOtherServiceImpl extends ServiceImpl<OrderProductOtherMapper, OrderProductOther> implements OrderProductOtherService {

    @Autowired
    private OrderUtils orderUtils;
    @Autowired
    private ShoppingCar shoppingCar;
    @Autowired
    private SessionUtil sessionUtil;
    @Autowired
    private PayService payService;
    @Autowired
    private ConfigService configService;
    @Autowired
    private MemberStoreService memberStoreService;
    @Autowired
    private MemberCacheService memberCacheService;
    @Autowired
    private ProductOtherService productOtherService;
    @Autowired
    private MemberAddressService memberAddressService;
    @Autowired
    private ProductPayTypeService productPayTypeService;
    @Autowired
    private OrderProductOtherDetailsService orderDetailsService;
    @Autowired
    private OrderProductOtherTotalService orderProductOtherTotalService;

    @Value("${productOtherOrderPrefix}")
    private String productOtherOrderPrefix;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseOrderRep orderProduct(OrderProductOtherCreateReq createReq) {
        LoginUserSession session = sessionUtil.getUser();

        if(session.getStoreFreezeStatus() == com.service.dao.enums.MemberEnums.StoreStatus.FROZEN.getCode()){
            throw new CommonException("账户资产已冻结，如有疑问请联系客服","account_assets_frozen");
        }
        MemberAddress memberAddress = memberAddressService.getById(createReq.getAddressId());
        if (ObjectUtils.isEmpty(memberAddress)) {
            throw new CommonException("收货地址不存在", "consignee_address_not_exit");
        }

        List<ProductOtherCar> shoppingCarList = Lists.newArrayList();
        Map<String, SysConfig> configMap = configService.getConfigMapByGroup(ConfigConstants.Group.mall.getCode());
        Map<String, BigDecimal> totalPriceMap = Maps.newHashMap();
        Map<Integer, BigDecimal> totalPriceMapByProductType = Maps.newHashMap();
        Map<Integer, BigDecimal> totalGiftMapByProductType = Maps.newHashMap();
        Map<Integer, BigDecimal> totalFreightByProductType = Maps.newHashMap();

        OrderProductOtherTotalVo totalVo = buildCoinConfByProductType(configMap);
        totalVo.setTotalPriceMapByProductType(totalPriceMapByProductType);
        totalVo.setTotalGiftMapByProductType(totalGiftMapByProductType);
        totalVo.setTotalFreightByProductType(totalFreightByProductType);

        for (OrderProductOtherReq item : createReq.getProductList()) {
            ProductOther productOther = productOtherService.getById(item.getProductId());
            if (!ObjectUtils.nullSafeEquals(productOther.getProductStatus(), CommonStatusEnums.ACTIVE.getCode())) {
                shoppingCar.removeProductFromCar(session.getUserId());
                throw new CommonException("商品已下架，请重新选购商品", "product_sold_out");
            }

            if (isMoreThenBuyNum(productOther, session.getUserId())) {
                throw new CommonException("该商品限购，已超出购买数量", "order_exception_more_then_limit_buy_num");
            }

            if (productOther.getStock() >= 0 && productOther.getBuyNum() > productOther.getStock()) {
                //TODO:是否给管理员发送告警
                throw new CommonException("商品库存不足", "product_stock_is_deficiency");
            }

            ProductOtherCar productOtherCar = ModelMapperUtil.getStrictModelMapper().map(productOther, ProductOtherCar.class);
            productOtherCar.setOrderNum(item.getBuyNum());

            productOtherCar.setProductPriceCoin(totalVo.getPayCoinsByProductType().get(productOther.getProductType()).getName());
            productOtherCar.setGiftCoins(totalVo.getGiftCoinsByProductType().get(productOther.getProductType()).getName());
            BigDecimal buyNum = new BigDecimal(item.getBuyNum());
            BigDecimal productPrice = productOther.getProductPrice().multiply(buyNum);
            //计算商品总金额
            totalPriceMap.put(productOtherCar.getProductPriceCoin(), totalPriceMap.getOrDefault(productOtherCar.getProductPriceCoin(), BigDecimal.ZERO).add(productPrice));
            totalPriceMapByProductType.put(productOther.getProductType(),
                    totalPriceMapByProductType.getOrDefault(productOther.getProductType(), BigDecimal.ZERO)
                            .add(productPrice));


            //计算总赠送货币数量
            totalGiftMapByProductType.put(productOther.getProductType(),
                    totalGiftMapByProductType.getOrDefault(productOther.getProductType(), BigDecimal.ZERO)
                            .add(new BigDecimal(productOtherCar.getGiftPoints()).multiply(buyNum)));

            productOtherCar.setFreightCoin(totalVo.getFreightCoins().getName());

            //计算商品配送费用
            totalPriceMap.put(productOtherCar.getFreightCoin(), totalPriceMap.getOrDefault(productOtherCar.getFreightCoin(), BigDecimal.ZERO).add(productOtherCar.getFreight()));
            totalFreightByProductType.put(productOther.getProductType(),
                    totalFreightByProductType.getOrDefault(productOther.getProductType(), BigDecimal.ZERO)
                            .add(productOtherCar.getFreight()));

            shoppingCarList.add(productOtherCar);
            productOtherService.updateProductOtherNum(productOther.getId(), item.getBuyNum());
        }

        checkMemberBalance(session.getUserId(), totalPriceMap);

        OrderProductOther orderProductOther = new OrderProductOther();
        orderProductOther.setOrderNo(buildShopOrderNo());
        orderProductOther.setMemberId(session.getUserId());
        orderProductOther.setMemberAccount(session.getAccount());

        orderProductOther.setOrderMoney(BigDecimal.ZERO);
        orderProductOther.setOrderType(0);
        orderProductOther.setGiftPoints(0);

        orderProductOther.setOrderStatus(OrderProductOtherStatus.NOT_PAY.getCode());
        orderProductOther.setConsigneeName(memberAddress.getConsigneeName());
        orderProductOther.setConsigneeMobile(memberAddress.getConsigneeMobile());
        orderProductOther.setConsigneeAddress(buildConsigneeAddress(memberAddress));
        orderProductOther.setCreateTime(new Date());
        baseMapper.insert(orderProductOther);

        totalVo.setOrderId(orderProductOther.getId());

        orderProductOtherTotalService.createdOrderTotal(totalVo);

        orderDetailsService.createProductOrderDetail(shoppingCarList, orderProductOther);

        return BaseOrderRep.buildProductOther(orderProductOther, totalPriceMap, orderUtils.getOvertime(orderProductOther.getCreateTime()));
    }

    private OrderProductOtherTotalVo buildCoinConfByProductType(Map<String, SysConfig> configMap) {

        Map<Integer, CoinsEnum> payCoinsMap = Maps.newHashMap();
        CoinsEnum coinsEnum = CoinsEnum.valueOf(configMap.get(ConfigConstants.GIFT_ORDER_COINS_ID).getConfOptions());
        payCoinsMap.put(ProductEnums.Type.other_gift.getCode(), coinsEnum);

        coinsEnum = CoinsEnum.valueOf(configMap.get(ConfigConstants.POINTS_ORDER_COINS_ID).getConfOptions());
        payCoinsMap.put(ProductEnums.Type.other_point.getCode(), coinsEnum);

        Map<Integer, CoinsEnum> giftCoinsMap = Maps.newHashMap();
        coinsEnum = CoinsEnum.valueOf(configMap.get(ConfigConstants.GIFT_ORDER_GIFT_COINS).getConfOptions());
        giftCoinsMap.put(ProductEnums.Type.other_gift.getCode(), coinsEnum);

        coinsEnum = CoinsEnum.valueOf(configMap.get(ConfigConstants.POINTS_ORDER_GIFT_COINS).getConfOptions());
        giftCoinsMap.put(ProductEnums.Type.other_point.getCode(), coinsEnum);

        coinsEnum = CoinsEnum.valueOf(configMap.get(ConfigConstants.FREIGHT_COINS_ID).getConfOptions());

        return OrderProductOtherTotalVo.builder()
                .payCoinsByProductType(payCoinsMap)
                .giftCoinsByProductType(giftCoinsMap)
                .freightCoins(coinsEnum)
                .build();
    }

    private void checkMemberBalance(Integer memberId, Map<String, BigDecimal> totalPrice) {
        totalPrice.keySet().forEach(item -> {
            if (!memberStoreService.enoughOfBalance(memberId, item, totalPrice.get(item))) {
                String msg = I18nUtil.getMessage("store_balance_exception_not_enough", Language.LANG.get(), CoinsEnum.EXDF.getName());
                throw new CommonException(msg);
            }
        });
    }

    /**
     * 是否超出购买限制
     *
     * @param productOther 产品
     * @param memberId     会员id
     * @return
     */
    private boolean isMoreThenBuyNum(ProductOther productOther, Integer memberId) {
        return productOther.getLimitBuyNum() > 0 && orderDetailsService.getProductOtherBuyNum(memberId, productOther.getId(), OrderProductOtherStatus.getBuyStatus()) >= productOther.getLimitBuyNum();
    }

    @Override
    public BaseOrderRep getOrderPayInfo(String orderNo) {
        final OrderProductOther productOther = getOrderByOrderNo4Pay(orderNo);
        Map<String, BigDecimal> totalPriceMap = orderProductOtherTotalService.listTotal4PayByOrderId2Map(productOther.getId());
        return BaseOrderRep.buildProductOther(productOther, totalPriceMap, orderUtils.getOvertime(productOther.getCreateTime()));
    }

    @Override
    public OrderProductOther getOrderByOrderNo4Pay(String orderNo) {
        OrderProductOther productOther = getOrderByOrderNo(orderNo);
        if (ObjectUtils.nullSafeEquals(productOther.getOrderStatus(), OrderProductOtherStatus.NOT_PAY_OVERTIME.getCode())) {
            log.error("获取订单<orderNo:{}>支付信息失败:此订单支付已取消", orderNo);
            throw new CommonException("获取支付信息失败，此订单支付已取消", "order_pay_info_exception_status_cancel");
        }

        if (OrderProductOtherStatus.getBuyStatus().contains(productOther.getOrderStatus())) {
            log.error("获取订单<orderNo:{}>支付信息失败:此订单已支付", orderNo);
            throw new CommonException("获取支付信息失败，此订单已支付", "order_pay_info_exception_status_payed");
        }

        if (orderUtils.isOvertime(productOther.getCreateTime())) {
            log.error("获取订单<orderNo:{}>支付信息失败:该订单已超时", orderNo);
            throw new CommonException("获取支付信息失败,该订单已超时", "order_pay_info_exception_over_time");
        }
        return productOther;
    }


    @Override
    public OrderProductOther getOrderByOrderNo(String orderNo) {
        final OrderProductOther productOther = getOne(new QueryWrapper<OrderProductOther>().lambda().eq(OrderProductOther::getOrderNo, orderNo));
        if (ObjectUtils.isEmpty(productOther)) {
            log.error("获取订单<orderNo:{}>详情:不存在此订单", orderNo);
            throw new CommonException("不存在此订单", "order_exception_not_exist");
        }
        return productOther;
    }

    private String buildConsigneeAddress(MemberAddress memberAddress) {
        StringBuilder stringBuffer = new StringBuilder();
        if (StringUtils.isNotBlank(memberAddress.getCountry())) {
            stringBuffer.append(memberAddress.getCountry());
        }
        if (StringUtils.isNotBlank(memberAddress.getProvince())) {
            stringBuffer.append(memberAddress.getProvince());
        }
        if (StringUtils.isNotBlank(memberAddress.getCity())) {
            stringBuffer.append(memberAddress.getCity());
        }
        if (StringUtils.isNotBlank(memberAddress.getCounty())) {
            stringBuffer.append(memberAddress.getCounty());
        }
        if (StringUtils.isNotBlank(memberAddress.getAddress())) {
            stringBuffer.append(memberAddress.getAddress());
        }
        return stringBuffer.toString();
    }

    @Override
    public String buildShopOrderNo() {
        String no = productOtherOrderPrefix + DateFormatUtils.format(new Date(), "yyyyMMdd") + UUIDUtil.randomUUID10().substring(0, 8);
        OrderProductOther orderProductOtherList = getOne(new QueryWrapper<OrderProductOther>().lambda()
                .eq(OrderProductOther::getOrderNo, no).last(" limit 1"));
        if (!ObjectUtils.isEmpty(orderProductOtherList)) {
            no = buildShopOrderNo();
        }
        return no;
    }

    @Override
    public CommonPageRep<OrderProductRep> pageOrder(OrderProductPageReq pageRequest) {

        Integer memberId = this.sessionUtil.getUser().getUserId();
        Page<OrderProductOther> page = PageHelper.startPage(pageRequest.getPageNo(), pageRequest.getPageSize());
        baseMapper.selectList(new QueryWrapper<OrderProductOther>().lambda().eq(OrderProductOther::getMemberId, memberId)
                .in(OrderProductOther::getOrderStatus, pageRequest.getOrderStatus())
                .orderByDesc(OrderProductOther::getCreateTime));


        Map<String, SysConfig> configMap = configService.getConfigMapByGroup(ConfigConstants.Group.mall.getCode());
        SysConfig config = configMap.get(ConfigConstants.ORDER_OVERDUE_TIME);

        if(ObjectUtils.isEmpty(page.getResult())){
            return CommonPageRep.buildResultRep(page, new ArrayList<>());
        }
        List<OrderProductRep> resultList = page.getResult().stream().map(item -> {
            OrderProductRep orderProductRep = ModelMapperUtil.getStrictModelMapper().map(item, OrderProductRep.class);
            if (ObjectUtils.nullSafeEquals(orderProductRep.getOrderStatus(), OrderProductOtherStatus.NOT_PAY.getCode())) {
                orderProductRep.setValidTime(DateUtil.getValidTime(item.getCreateTime(), Integer.parseInt(config.getConfValue())));
            }

            buildOrderDetailTotal(item, orderProductRep, configMap);

            return orderProductRep;
        }).collect(Collectors.toList());

        return CommonPageRep.buildResultRep(page, resultList);
    }

    private boolean checkGiftCoin(String key, Map<String, SysConfig> configMap) {
        return StringUtils.equals(key, configMap.get(ConfigConstants.GIFT_ORDER_GIFT_COINS).getConfOptions())
                || StringUtils.equals(key, configMap.get(ConfigConstants.POINTS_ORDER_GIFT_COINS).getConfOptions());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cancelOrder(String orderNo) {

        OrderProductOther existOrder = getOne(new QueryWrapper<OrderProductOther>()
                .lambda().eq(OrderProductOther::getOrderNo, orderNo));
        if (ObjectUtils.isEmpty(existOrder)) {
            throw new CommonException("取消失败，没有找到订单", "cancel_order_fail_by_empty_order");
        }
        cancelOrder(existOrder.getId());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cancelOrder(Integer orderId) {
        OrderProductOther orderProductOther = new OrderProductOther();
        orderProductOther.setId(orderId);
        orderProductOther.setOrderStatus(HashRateOrderEnums.Status.noneffective.getCode());
        updateById(orderProductOther);
        addProductOtherStockNum(orderId);
    }

    private void addProductOtherStockNum(Integer orderId) {
        List<OrderProductOtherDetails> detailsList = orderDetailsService.list(new QueryWrapper<OrderProductOtherDetails>().lambda().eq(OrderProductOtherDetails::getOrderId, orderId));
        detailsList.forEach(item -> {
            productOtherService.updateProductOtherNum(item.getProductId(), item.getOrderNum() * -1);
        });
    }

    @Override
    public OrderProductRep orderDetail(String orderNo) {
        OrderProductOther orderProductOther = getOrderByOrderNo(orderNo);

        OrderProductRep orderProductRep = ModelMapperUtil.getStrictModelMapper()
                .map(orderProductOther, OrderProductRep.class);

        orderProductRep.setProductList(orderDetailsService.listByOrderId(orderProductOther.getId()));

        Map<String, SysConfig> configMap = configService.getConfigMapByGroup(ConfigConstants.Group.mall.getCode());

        buildOrderDetailTotal(orderProductOther, orderProductRep, configMap);

        if (ObjectUtils.nullSafeEquals(orderProductRep.getOrderStatus(), OrderProductOtherStatus.NOT_PAY.getCode())) {
            SysConfig config = configMap.get(ConfigConstants.ORDER_OVERDUE_TIME);
            orderProductRep.setValidTime(DateUtil.getValidTime(orderProductOther.getCreateTime(), Integer.parseInt(config.getConfValue())));
        }

        return orderProductRep;
    }

    private void buildOrderDetailTotal(OrderProductOther orderProductOther, OrderProductRep orderProductRep, Map<String, SysConfig> configMap) {
        Map<String, BigDecimal> totalMap = orderProductOtherTotalService.listTotalByOrderId2Map(orderProductOther.getId());

        Map<String, BigDecimal> payMoney = totalMap.entrySet().stream()
                .filter(map -> !(checkGiftCoin(map.getKey(), configMap)))
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

        Map<String, BigDecimal> giftNumMap = totalMap.entrySet().stream()
                .filter(map -> checkGiftCoin(map.getKey(), configMap))
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

        orderProductRep.setTotalMoney(payMoney);
        orderProductRep.setGiftMap(giftNumMap);
    }

    /**
     * 获取有效订单金额
     *
     * @param userId
     * @return
     */
    @Override
    public BigDecimal getGiftOrderTotalMoney(Integer userId) {
        return baseMapper.getGiftOrderTotalMoney(userId, OrderProductOtherStatus.getBuyStatus());
    }

    @Override
    public BigDecimal getGiftOrderTotalMoneyByTime(Integer userId, Date startTime, Date endTime) {
        return baseMapper.getSomedayGiftOrderTotalMoney(userId, OrderProductOtherStatus.getBuyStatus(), startTime, endTime);
    }

    @Override
    public BigDecimal getOrderTotalMoneyFromCache(Integer memberId) {
        MemberCache memberCache = memberCacheService.getMemberCache(memberId);
        if (ObjectUtils.isEmpty(memberCache)) {
            memberCache.setTotalConsumption(getGiftOrderTotalMoney(memberId));
            memberCacheService.setMemberCache(memberCache);
        }
        return memberCache.getTotalConsumption();
    }

    @Override
    @Transactional
    public void releaseRecommendAward(Integer orderId) {
        OrderProductOther orderProductOther = getById(orderId);
        if (ObjectUtils.isEmpty(orderProductOther)) {
            throw new CommonException("不存在此订单");
        }
        orderProductOther = new OrderProductOther();
        orderProductOther.setId(orderId);
        orderProductOther.setReleaseAward(OrderReleaseAwardEnum.RELEASE.getCode());
        updateById(orderProductOther);
    }

    @Override
    public BigDecimal getSomedayGiftOrderTotalMoney(Integer memberId, Date someday) {
        Date someDayStartTime = DateUtil.getDayOfStart(someday);
        Date someDayEndTime = DateUtil.getDayOfEnd(someday);

        return baseMapper.getSomedayGiftOrderTotalMoney(memberId, OrderProductOtherStatus.getBuyStatus(), someDayStartTime, someDayEndTime);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void sureReceive(String orderNo) {
        OrderProductOther productOther = getOrderByOrderNo(orderNo);
        if (!ObjectUtils.nullSafeEquals(productOther.getOrderStatus(), OrderProductOtherStatus.DELIVERED.getCode())) {
            throw new CommonException("确认收货失败，订单状态错误", "order_receive_exception_status_error");
        }
        updateById(OrderProductOther.builder()
                .id(productOther.getId())
                .orderStatus(OrderProductOtherStatus.COMPLETED.getCode())
                .build());
    }
}
